package cn.well.cloud.config.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import cn.well.cloud.core.auth.deniedhandler.JwtAuthenticationAccessDeniedHandler;
import cn.well.cloud.core.auth.entrypoint.JwtAuthenticationEntryPoint;
import cn.well.cloud.core.auth.filter.JwtAuthenticationTokenFilter;
import cn.well.cloud.core.auth.userdetail.JwtUserDetailsServiceImpl;


/**
 * @author 
 * @description SpringSecurity的配置,解决csrf校验开启问题,druid登陆界面无效的解决
 * @modified By
 *
 * |@EnableGlobalMethodSecurity(prePostEnabled=true) 开启注解，使用户对某个controller层的方法具有访问权限
 * |Controller层上加上@PreAuthorize("hasAuthority('pms:brand:read')")注解，用来判断当前用户是否有读的权限
 * |用户账号密码登录之后，从数据库查找当前用户的权限放入Authentication对象中
 * |SecurityExpressionRoot类中的getAuthoritySet()方法可以获取当前用户的权限集合
 *
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
 
    @Autowired
    private JwtUserDetailsServiceImpl userDetailsService;
    
    //当访问接口没有权限时，自定义的返回结果
    @Autowired
    private JwtAuthenticationAccessDeniedHandler accessDeniedHandler;
    
    //当未登录或者token失效访问接口时，自定义的返回结果
    @Autowired
    private JwtAuthenticationEntryPoint unauthorizedEntryPoint;
    
    //token过滤器
    @Autowired
    private JwtAuthenticationTokenFilter authenticationTokenFilter;
 
    /**
     * 用于配置需要拦截的url路径、jwt过滤器及出异常后的处理器.
     * @param httpSecurity
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                //关闭CSRF
                .csrf().disable() 
                //开启跨域
                .cors().and() 
                // 基于token，所以不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
                .antMatchers("/resources/**", "/home","/**/favicon.ico","/auth/*","/swagger-resources/**").permitAll()
                //rest方式获取token入口
                .antMatchers("/auth/login").permitAll()
                //跨域请求会先进行一次options请求
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                //允许访问druid监控页面，由于CSRF跨站点请求伪造(Cross—Site Request Forgery)的原因，会进不去druid监控页面
                .antMatchers("/druid/*").permitAll()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated();
                // 禁用缓存
                httpSecurity.headers().cacheControl();
                // 添加JWT filter
                httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
                // 添加自定义未授权和未登录结果返回
                httpSecurity.exceptionHandling().accessDeniedHandler(accessDeniedHandler) //未授权
                .authenticationEntryPoint(unauthorizedEntryPoint);//未登录
    }
 
    /**
     * 用于配置UserDetailsService及PasswordEncoder
     * 2019年12月23日
     * @param auth
     * @throws Exception
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }
    
}